using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Runtime.InteropServices;

namespace GuavaLauncher
{
    [ComVisible(true), ClassInterface(ClassInterfaceType.None)]
    public class GuavaLauncher : SPARCEContextCore.ContextAgent
    {
        SPARCECentral.Application app;
        SPARCECentral.Container cont;
        SPARCECentral.Mark mark;
        SPARCEContextCore.ContextAwareObject cao;
        string path;
        GuavaData.Channel channel;

        ConstructorInfo ci;
        Type appType;
        string seed;
        GuavaData.GSeed gs;
        GuavaData.Guava guava;

        void CreateConstructor(string form)
        {
            // Boilerplate
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            // Pull the correct assembly
            Assembly app = Assembly.LoadFile(path);

            // Pull the Program class from the assembly
            appType = app.GetType(form, true);
            if (appType == null) throw new Exception("Could not find the appropriate entry point class in application.");

            foreach (Type t in app.GetTypes())
            {
                if (Attribute.IsDefined(t, typeof(GuavaLib.ChannelAttribute), false))
                {
                    foreach (MethodInfo mi in t.GetMethods())
                    {
                        if (Attribute.IsDefined(mi, typeof(GuavaLib.ChannelAttribute)))
                        {
                            channel = (GuavaData.Channel) (mi.Invoke(null, new object[] { }));
                        }
                    }
                }
            }

            // Initialize constructor
            ci = appType.GetConstructor(new Type[0]);
        }

        void LaunchApplication()
        {
            Object o = ci.Invoke(new object[0]);
            Guid g = new Guid();
            GuavaData.AppData.Init(o as GuavaData.GuavaForm, g, path, gs.source, gs.catalog, channel);

            // Run application
            ConstructorInfo c = appType.GetConstructor(new Type[] { typeof(Guid), typeof(Guid), typeof(bool), typeof(GuavaData.GSeed) });
            o = c.Invoke(new object[] { g, new Guid(), false, gs });
            Application.Run(o as GuavaLib.GForm);

            gs = GuavaData.GSeed.FromString(seed);
        }

        #region _ContextAgent Members

        public SPARCEContextCore.ContextAwareObject Client
        {
            get { return cao; }
        }
        
        public void Construct(ref SPARCEContextCore.ContextAwareObject oClient)
        {
            string form = null;

            this.cao = oClient;
            if (oClient is SPARCECentral.Application)
            {
                app = oClient as SPARCECentral.Application;
            }
            else if (oClient is SPARCECentral.Container)
            {
                cont = oClient as SPARCECentral.Container;
                app = cont.Application;
                path = cont.Location;
                form = cont.ClassID;
            }
            else if (oClient is SPARCECentral.Mark)
            {
                mark = oClient as SPARCECentral.Mark;
                cont = mark.Container;
                app = cont.Application;
                path = cont.Location;
                form = cont.ClassID;

                // Pull the address, build the g-tree and pull the context info
                seed = mark.Address;
                gs = GuavaData.GSeed.FromString(seed);
            }
            if (cont != null) CreateConstructor(form);
            if (mark != null)
            {
                GuavaData.Guava o = ci.Invoke(new object[0]) as GuavaData.Guava;
                bool first = true;
                foreach (string s in gs.ToList())
                {
                    if (first)
                    {
                        first = false;
                        continue;
                    }
                    o = o[s];
                }
                this.guava = o;
            }
        }

        public SPARCEContextCore.Context Context
        {
            get
            {
                SPARCEContextCore.ContextAgent aTemp = this;
                SPARCEContextCore.ContextKind kTemp = new SPARCEContextCore.ContextKindClass();
                SPARCEContextCore.ContextKind kRoot = new SPARCEContextCore.ContextKindClass();
                SPARCEContextCore.Context context = new SPARCEContextCore.ContextClass();
                SPARCEContextCore.ContextElement ce;
                Dictionary<string, object> d = new Dictionary<string, object>();
                string sKind;
                object o;

                kRoot.Construct(ref aTemp, "Root".GetHashCode(), "Root");
                if (mark != null)
                {
                    // Mark context
                    sKind = "Guava Element Context";
                    kTemp.Construct(ref aTemp, sKind.GetHashCode(), sKind);
                    d.Add("Text", this.guava.GText);
                    d.Add("Name", this.guava.GName);
                    d.Add("Domain", this.guava.GDomain.ToString());
                    d.Add("Default Value", this.guava.GDefault);
                    d.Add("ToolTip", this.guava.GToolTip);
                    foreach (string s in d.Keys)
                    {
                        o = d[s];
                        ce = new SPARCEContextCore.ContextElementClass();
                        ce.Construct(ref aTemp, sKind.GetHashCode(), s.GetHashCode(), s, SPARCEContextCore.EContextElementNavigability.ecenNone, ref o);
                        kTemp.AddElement(ref ce);
                    }
                }
                else if (cont != null)
                {
                    // Container context
                    sKind = "Guava Application Context";
                    kTemp.Construct(ref aTemp, sKind.GetHashCode(), sKind);
                    d.Add("Location", path);
                    foreach (string s in d.Keys)
                    {
                        o = d[s];
                        ce = new SPARCEContextCore.ContextElementClass();
                        ce.Construct(ref aTemp, sKind.GetHashCode(), s.GetHashCode(), s, SPARCEContextCore.EContextElementNavigability.ecenNone, ref o);
                        kTemp.AddElement(ref ce);
                    }
                }
                else
                {
                    // Application context
                    sKind = "Guava Framework Context";
                    kTemp.Construct(ref aTemp, sKind.GetHashCode(), sKind);
                    d.Add("Version", "0.9.0.0");
                    foreach (string s in d.Keys)
                    {
                        o = d[s];
                        ce = new SPARCEContextCore.ContextElementClass();
                        ce.Construct(ref aTemp, sKind.GetHashCode(), s.GetHashCode(), s, SPARCEContextCore.EContextElementNavigability.ecenNone, ref o);
                        kTemp.AddElement(ref ce);
                    }
                }

                context.Construct(ref aTemp, ref kRoot);
                kRoot.AddKind(ref kTemp);
                return context;
            }
        }

        public object ElementValue(int nKindID, int nElementID, SPARCEContextCore.EContextValueSource nSource)
        {
            SPARCEContextCore.CString cs = new SPARCEContextCore.CStringClass();
            string sTemp;
            if (nKindID == "Guava Element Context".GetHashCode())
            {
                if (nElementID == "Text".GetHashCode()) sTemp = this.guava.GText;
                else if (nElementID == "Name".GetHashCode()) sTemp = this.guava.GName;
                else if (nElementID == "Domain".GetHashCode()) sTemp = this.guava.GDomain.ToString();
                else if (nElementID == "Default Value".GetHashCode()) sTemp = this.guava.GDefault;
                else if (nElementID == "ToolTip".GetHashCode()) sTemp = this.guava.GToolTip;
                else throw new Exception("Guava does not understand an ElementID of " + nElementID.ToString());
            }
            else if (nKindID == "Guava Application Context".GetHashCode())
            {
                if (nElementID == "Location".GetHashCode()) sTemp = path;
                throw new Exception("Guava does not understand an ElementID of " + nElementID.ToString());
            }
            else if (nKindID == "Guava Framework Context".GetHashCode())
            {
                if (nElementID == "Version".GetHashCode()) sTemp = "0.9.0.0";
                throw new Exception("Guava does not understand an ElementID of " + nElementID.ToString());
            }
            else throw new Exception("Guava does not understand a KindID of " + nKindID.ToString());
            cs.value = sTemp;
            return cs;
        }

        public void Navigate()
        {
            if (cont != null) this.LaunchApplication();
            else MessageBox.Show("Guava does not have a native interface.");
        }

        public void NavigateToElement(int nKindID, int nElementID, ref object oExtra)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public object Object
        {
            get
            {
                if (mark != null) return mark;
                if (cont != null) return cont;
                return app;
            }
        }

        #endregion
    }
}